问题描述:在设置–添加账号菜单多出Exchange重复账号 总共3个exchange账号条目,且功能相同。概率性问题,恢复出厂设置之后,可能就会出现(1/8)且有时候还会出现两个相同的exchange账号。
1、问题分析:
1)/Settings/src/com/android/settings/accounts/ChooseAccountActivity.java
出现异常时,settings模块的log中 关于pref.type的信息如下:
1 | Line 19710: 01-01 07:00:19.379 6703 6703 I ChooseAccountActivity: NHideAccountItem: account type =com.android.exchange hide=false |
可以看出并没有重复的账户类型,那么,当pref.type正常时,为什么会出现账户图标出错的问题,发现整个preference的图标,名称的获取是从下面这行代码中得到的,然后就从/Settings/src/com/android/settings/accounts/ChooseAccountActivity.java
出发跟踪了一下这部分代码。
1 | mAuthDescs = AccountManager.get(this).getAuthenticatorTypesAsUser(mUserHandle.getIdentifier()); |
在这个方法中,查看了mService的类型,如下:
private final IAccountManager mService;
根据IPC机制,我们跳转到AccountManagerService这个类。
3)frameworks/base/services/core/java/com/android/server/accounts/AccountManagerService.java
在getAuthenticatorTypes()方法中,主要为下面这句:
return getAuthenticatorTypesInternal(userId);
然后跳转到getAuthenticatorTypesInternal()方法。
1 | private AuthenticatorDescription[] getAuthenticatorTypesInternal(int userId) { |
然后我们可以推断出,其资源的加载还在这行代码中:
authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
又因为:
private final IAccountAuthenticatorCache mAuthenticatorCache;
跳转至AccountAuthenticatorCache类。
4)/frameworks/base/services/core/java/com/android/server/accounts/AccountAuthenticatorCache.java
然后发现这个类,并没有getAllServices()方法,因为其继承自 RegisteredServicesCache类,所有转去 RegisteredServicesCache类查看其getAllServices()方法。
5)/frameworks/base/core/java/android/content/pm/RegisteredServicesCache.java
1 | /** |
根据其方法解释,我们大体可以理解它的具体功能,然后我们进入generateServicesMap(null, userId)方法。
发现在其中有这样一句:
final List<ResolveInfo> resolveInfos = queryIntentServices(userId);
其实现为:
1 | protected List<ResolveInfo> queryIntentServices(int userId) { |
根据名称,我们不难猜出,它是一个根据特定intent,userid查询相关操作的service,并将该service的
那么我们就得看一下这个mInterfaceName是什么呢?
搜索了一下,发现它是在RegisteredServicesCache类初始化的时候被赋值的,因为RegisteredServicesCache类在这个分析链中是被AccountAuthenticatorCache继承的,然后我们就得回到AccountAuthenticatorCache类了,看看它的初始化
6)/frameworks/base/services/core/java/com/android/server/accounts/AccountAuthenticatorCache.java
AccountAuthenticatorCache的初始化如下:
1 | public AccountAuthenticatorCache(Context context) { |
然后我们回到一开始的AccountManager类,发现在其中有这样几行代码:
1 | public static final String ACTION_AUTHENTICATOR_INTENT = |
然后结合new Intent(mInterfaceName) =new Intent(“android.accounts.AccountAuthenticator”)。
又到了展现曾工工具强大的地方了,我们搜索了一下android.accounts.AccountAuthenticator
,然后发现在/packages/apps/Email/AndroidManifest.xml
中,注册了八个存在这样<action>
的service
1 | <intent-filter> |
然后看了看他们的
1 | <meta-data |
点开/packages/apps/Email/res/xml/authenticator_pop3.xml
1 | <account-authenticator xmlns:android="http://schemas.android.com/apk/res/android" |
对比其图标,账户类型等信息,确实是Settings模块中account的图标和账户。
7)/frameworks/base/core/java/android/content/pm/RegisteredServicesCache.java
然后就接着回到了generateServicesMap()方法中的。
1 | final List<ResolveInfo> resolveInfos = queryIntentServices(userId); |
发现之后,就是这样的一个循环:
1 | for (ResolveInfo resolveInfo : resolveInfos) { |
于是接着看parseServiceInfo()方法,发现在其中有这样一个关键方法;
1 | V v = parseServiceAttributes(pm.getResourcesForApplication(si.applicationInfo), |
因为AccountAuthenticatorCache类中,有对这个方法进行重写,具体内容如下:
1 | public AuthenticatorDescription parseServiceAttributes(Resources res, |
其中需要解释的是这个数组:
1 | public TypedArray obtainAttributes (AttributeSet set, int[] attrs)(说明此函数) |
那么具体的表现可以联想一下这两部分:
<1>1)public static final String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
2) /packages/apps/Email/res/xml/authenticator_pop3.xml中的account-authenticator标签中间的内容。
<2>
1 | <declare-styleable name="AccountAuthenticator"> |
分析到此,基本可以结束,由于没有全局去看,基本是按照一个链条在走,所以还有是有一些疑惑的地方,如果大家在看的时候有什么新的见解,可以告诉我一块研究研究。
2、总结:
1)AccountAuthenticatorCache是Android平台中账户验证服务(Account AuthenticatorService,AAS)的管理中心。而AAS则由应用程序通过在AndroidManifest.xml中输出符合指定要求的Service信息而来。
2)RegisteredServicesCache是一个模板类,专门用于管理系统中指定Service的信息收集和更新,而具体是哪些Service由RegisteredServicesCache构造时的参数指定。而AccountAuthenticatorCache从RegisteredServicesCache
3)在修改了framework部分的代码,之后,想在超级服务器上进行本地验证,可以讲修改的模块单独编译,比如编译service模块,可以用:
1 | source build.sh 项目名 services -j32 |
修改了framework/base下的文件,可以进入framework/base目录下使用:mm
命令编译
编译完成之后,进入out/target/project/特定名称/system/framework/目录下,查看其时间,将所有编译影响到的文件都在手机中进行替换即可。
2>1>